#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define BUFFER_SIZE 1024
uint16_t Default_port = 8888;

enum
{
    SOCKFD_ERROR = 1,
    BIND_ERROR,
    USEAGE_ERROR
};

class UPDServer
{
public:
    UPDServer(uint16_t port = Default_port)
        : _port(port), _sockfd(-1)
    {}

    // 初始化服务器：填充网络信息、bind
    void InitServer()
    {
        // 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd == -1)
        {
            printf("sockfd error: %s : %d\n", strerror(errno), errno);
            exit(SOCKFD_ERROR);
        }
        // 填充网络地址
        struct sockaddr_in serverAddr;
        memset(&serverAddr, 0, sizeof(struct sockaddr_in));
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(_port);
        serverAddr.sin_addr.s_addr = INADDR_ANY;

        // 绑定
        int n = bind(_sockfd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_in));
        if(n == -1)
        {
            printf("server bind error: %s : %d\n", strerror(errno), errno);
            exit(BIND_ERROR);
        }

    }

    void StartServer()
    {
        // 循环接收客户端的消息，并将处理消息
        char buffer[BUFFER_SIZE];

        // 存储客户端地址
        struct sockaddr_in ClientAddr;
        socklen_t len = sizeof(struct sockaddr_in);
        for(;;)
        {
            int RecvBytes = recvfrom(_sockfd, buffer, BUFFER_SIZE - 1, 0, (struct sockaddr*)&ClientAddr, &len);
            if(RecvBytes == -1)
            {
                printf("server receive message error: %s : %d\n", strerror(errno), errno);
            }
            buffer[RecvBytes] = 0;
            uint16_t ClientPort = ntohs(ClientAddr.sin_port);
            std::string ClientIP = inet_ntoa(ClientAddr.sin_addr);
            std::string recver = ClientIP + " : " + std::to_string(ClientPort);

            printf("%s # %s\n", recver.c_str(), buffer);

            // 向客户端发送消息
            std::string tmp = buffer;
            tmp  = "[server echo] " + tmp;
            int SendBytes = sendto(_sockfd, tmp.c_str(), strlen(tmp.c_str()), 0, (struct sockaddr*)&ClientAddr, len);
            if(SendBytes == -1)
            {
                printf("server send message error: %s : %d\n", strerror(errno), errno);
            }

        }
    }
    ~UPDServer()
    {
        
    }
private:
    uint16_t _port;
    int _sockfd;
};

void Usage(const char* proc)
{
    std::cout << "Usage: \n\t" << proc  << " local_port\n" << std::endl;
}

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return USEAGE_ERROR;
    }
    uint16_t port = std::atoi(argv[1]);
    
    std::unique_ptr<UPDServer> server = std::make_unique<UPDServer>(port);
    server->InitServer();
    server->StartServer();
    return 0;
}